/*
 * uartintf.cpp
 *
 *  Created on: 2021年8月23日
 *      Author: vicent-PC
 */

#include "uartintf.h"

/* UART asynchronous example, that uses separate RX and TX tasks

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "string.h"
#include "driver/gpio.h"

static const int RX_BUF_SIZE = 1024 * 8;
static const int TX_BUF_SIZE = 1024;
static const int BUF_SIZE = 1024;

#define TAG "uart"
#define TXD_PIN (GPIO_NUM_4)
#define RXD_PIN (GPIO_NUM_5)
const int uart_num = UART_NUM_0;
QueueHandle_t uart0_queue;

struct RxTaskParam
{
    /* data */
    ConsoleService *consoleHandler;
    void *cb;
};


void uart_task(void *pvParameters)
 {
     int uart_num = (int) pvParameters;
     uart_event_t event;
     size_t buffered_size;
     uint8_t* dtmp = (uint8_t*) malloc(BUF_SIZE);
     for(;;) {
         //Waiting for UART event.
         if(xQueueReceive(uart0_queue, (void * )&event, (portTickType)portMAX_DELAY)) {
             ESP_LOGI(TAG, "uart[%d] event.type:", uart_num, event.type);
             vTaskDelay(2000 / portTICK_PERIOD_MS);
             switch(event.type) {
                 //Event of UART receving data
                 /*We'd better handler data event fast, there would be much more data events than
                 other types of events. If we take too much time on data event, the queue might
                 be full.
                 in this example, we don't process data in event, but read data outside.*/
                 case UART_DATA:
                     uart_get_buffered_data_len(uart_num, &buffered_size);
                     ESP_LOGI(TAG, "data, len: %d; buffered len: %d", event.size, buffered_size);
                     break;
                 //Event of HW FIFO overflow detected
                 case UART_FIFO_OVF:
                     ESP_LOGI(TAG, "hw fifo overflow\n");
                     //If fifo overflow happened, you should consider adding flow control for your application.
                     //We can read data out out the buffer, or directly flush the rx buffer.
                     uart_flush(uart_num);
                     break;
                 //Event of UART ring buffer full
                 case UART_BUFFER_FULL:
                     ESP_LOGI(TAG, "ring buffer full\n");
                     //If buffer full happened, you should consider encreasing your buffer size
                     //We can read data out out the buffer, or directly flush the rx buffer.
                     uart_flush(uart_num);
                     break;
                 //Event of UART RX break detected
                 case UART_BREAK:
                     ESP_LOGI(TAG, "uart rx break\n");
                     break;
                 //Event of UART parity check error
                 case UART_PARITY_ERR:
                     ESP_LOGI(TAG, "uart parity error\n");
                     break;
                 //Event of UART frame error
                 case UART_FRAME_ERR:
                     ESP_LOGI(TAG, "uart frame error\n");
                     break;
                 //UART_PATTERN_DET
                 case UART_PATTERN_DET:
                     ESP_LOGI(TAG, "uart pattern detected\n");
                     break;
                 //Others
                 default:
                     ESP_LOGI(TAG, "uart event type: %d\n", event.type);
                     break;
             }
         }
     }
     free(dtmp);
     dtmp = NULL;
     vTaskDelete(NULL);
 }


void init(void) {
    const uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
		.rx_flow_ctrl_thresh = 122,
        .source_clk = UART_SCLK_APB,
    };
    // We won't use a buffer for sending data.
    // uart_driver_install(uart_num, RX_BUF_SIZE * 2, TX_BUF_SIZE, 10, &uart0_queue, 0);
    if (uart_is_driver_installed(uart_num)) {
        uart_driver_delete(uart_num);
    }
    uart_driver_install(uart_num, RX_BUF_SIZE * 2, TX_BUF_SIZE, 0, nullptr, 0);
    uart_param_config(uart_num, &uart_config);
    //uart_set_pin(uart_num, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    //Set uart pattern detect function.
    //uart_enable_pattern_det_intr(uart_num, '+', 3, 10000, 10, 10);
    // xTaskCreate(uart_task, "uart_task", 2048, (void*)uart_num, 12, NULL);
}

int sendData(const char* logName, const char* data)
{
    const int len = strlen(data);
    const int txBytes = uart_write_bytes(uart_num, data, len);
    ESP_LOGI(logName, "Wrote %d bytes", txBytes);
    return txBytes;
}

static void tx_task(void *arg)
{
    static const char *TX_TASK_TAG = "TX_TASK";
    esp_log_level_set(TX_TASK_TAG, ESP_LOG_INFO);
    while (1) {
        sendData(TX_TASK_TAG, "Hello world");
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
    vTaskDelete(NULL);
}

static void rx_task(void *args)
{
    static const char *RX_TASK_TAG = "RX_TASK";
    esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
    uint8_t* data = (uint8_t*) malloc(RX_BUF_SIZE + 1);
    memset(data, 0, RX_BUF_SIZE+1);

    RxTaskParam localTaskParam = *(RxTaskParam *)args;

    while (1) {
        int length = RX_BUF_SIZE;
        ESP_ERROR_CHECK(uart_get_buffered_data_len(uart_num, (size_t*)&length));
        length = uart_read_bytes(uart_num, data, RX_BUF_SIZE, 100 / portTICK_PERIOD_MS);
        if (length < 0) {
        	ESP_LOGI(RX_TASK_TAG, "Err uart_read_bytes ret:%d", length);
        	vTaskDelay(1000 / portTICK_PERIOD_MS);
        }

        if (length > 0) {
            data[length] = 0;
            ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", length, data);
            if (localTaskParam.cb != nullptr) {
                //callback
                // void (*pFunc)(uint8_t *) = (void (*)(uint8_t *))(localTaskParam.cb);
                // (*pFunc)(data);
                localTaskParam.consoleHandler->processCmd((const char *)data);
                // ESP_LOG_BUFFER_HEXDUMP(RX_TASK_TAG, data, rxBytes, ESP_LOG_INFO);
            }
            vTaskDelay(500 / portTICK_PERIOD_MS);
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    free(data);
    vTaskDelete(NULL);
}

void uart_main(void)
{
    init();
    xTaskCreate(rx_task, "uart_rx_task", 1024*2, NULL, 1, NULL);
    xTaskCreate(tx_task, "uart_tx_task", 1024*2, NULL, 1, NULL);
}


UartIntf::UartIntf() {
	// TODO Auto-generated constructor stub
}

UartIntf::~UartIntf() {
	// TODO Auto-generated destructor stub
}

void UartIntf::start(void *func_cb) {
    init();

    RxTaskParam taskParam;
    taskParam.cb = func_cb;
    taskParam.consoleHandler = mpConsoleHandler;

    xTaskCreate(rx_task, "uart_rx_task", 1024*8, &taskParam, 1, NULL);
    // xTaskCreate(tx_task, "uart_tx_task", 1024*2, NULL, configMAX_PRIORITIES-1, NULL);
}

int UartIntf::closeUarts(){
    for (int i = 0; i < UART_NUM_MAX; i++) {
        if (uart_is_driver_installed(i)) {
            uart_driver_delete(i);
        }
    }
    return 0;
}